<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0,minimum-scale=1,maximum-scale=1,user-scalable=no">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <!-- <link rel="stylesheet" type="text/css" href="css/index.css">
    <script type="text/javascript" src="js/index.js"></script>
    <style type="text/css"></style> -->
    <title>Document</title>
    <style>
        div{
            width: 100px;
            height: 100px;
            background-color: red;
            position: absolute;
        }
        .box2{
            top: 110px;
        }
    </style>
</head>

<body>
    <div class="box1"></div>
    <div class="box2"></div>
    <script>
        /* window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画，并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
        该方法需要传入一个回调函数作为参数，该回调函数会在浏览器下一次重绘之前执行
        注意：若你想在浏览器下次重绘之前继续更新下一帧动画，那么回调函数自身必须再次调用window.requestAnimationFrame()
        当你准备更新动画时你应该调用此方法。这将使浏览器在下一次重绘之前调用你传入给该方法的动画函数(即你的回调函数)。
        回调函数执行次数通常是每秒60次，但在大多数遵循W3C建议的浏览器中，回调函数执行次数通常与浏览器屏幕刷新次数相匹配。 */

        //使用这个方法的好处是该方法帧动画与浏览器的刷新频率是保持一致的，但是IE10之前的浏览器不支持

        /**
         * @param element 待添加运动动画效果的DOM元素
         * @param attr 运动属性，如：'top', 'left', 'width', 'height'
         * @param end 运动的目标终点值
         * @param duration 限定运动的总时长
         */
        function animate(element,attr,end,duration){
            //停止动画的方法
            window.cancelAnimationFrame(element.timer)//element.timer表示将定时器绑定到元素身上，当有多个定时器的时候，不同的元素就有自己专属的定时器不会导致冲突
            //通过获取元素的外部样式属性来获取元素的初始状态
            const start=parseFloat(window.getComputedStyle(element,null)[attr])
            //元素的变化的动画速度
            const speed=(end-start)/duration
            //拿到定时器启动前的时间
            const startTime=Date.now()
            //运动一次的方法
            function move(){
                 //获取元素已经运动了多少时间
                 const elapsed=Math.min(duration,Date.now()-startTime)//因为定时器是每1000/60毫秒执行一次，所以定时器结束时Date.now()可能会大于总时长，所以这里取较小的那一个值
                //拿到元素最终的位置
                const result=elapsed*speed+start
                //设置元素的样式
                element.style[attr]=result+'px'
                //判断定时器启动的条件
                if(elapsed!==duration){
                    element.timer=window.requestAnimationFrame(move)
                }
            }
            //执行move(),实现运动效果
            move()
        }
        document.querySelector('.box1').addEventListener('mouseenter',function(e){
            animate(e.target,'width',500,1000)
        },false)
        document.querySelector('.box1').addEventListener('mouseleave',function(e){
            animate(e.target,'width',100,1000)
        },false)
        document.querySelector('.box2').addEventListener('mouseenter',function(e){
            animate(e.target,'width',400,1000)
        },false)
        document.querySelector('.box2').addEventListener('mouseleave',function(e){
            animate(e.target,'width',100,1000)
        },false)
    </script>
</body>

</html>